export default class requestCache {
  static nameLimit = 2048

  get cacheMap() {
    if (!this._cacheMap) this._cacheMap = new Map()
    return this._cacheMap
  }

  add(config, request) {
    const name = this.getCacheName(config)
    if (!name) return
    if (!request || !request.then) return

    if (this.has(name)) {
      console.warn('requestCache add cache error: cache existing')
      return
    }

    // 缓存时间
    const time = this.getCacheTime(config)
    // 缓存对象
    const cache = {
      name,
      time,
      config,
      request
    }

    // 新增缓存
    this.cacheMap.set(name, cache)

    const self = this
    if (time) {
      // 设置缓存时间, 则设置定时器删除缓存
      setTimeout(() => self.delete(name), time)
    } else {
      // 未设置缓存时间, 则请求结束时删除缓存
      if (request.finally) request.finally(_ => self.delete(name))
      else request.then(_ => self.delete(name)).catch(_ => self.delete(name))
    }

    return cache
  }

  has(config) {
    const name = this.getCacheName(config)
    return name ? this.cacheMap.has(name) : false
  }

  get(config) {
    const name = this.getCacheName(config)
    if (!this.has(name)) return null

    const cache = this.cacheMap.get(name)
    if (cache && cache.request) return cache

    this.delete(name)
    return null
  }

  delete(config) {
    const name = this.getCacheName(config)
    if (!this.has(name)) return

    let cache = this.cacheMap.get(name)
    if (cache) {
      cache.request = null
      cache = null
    }
    this.cacheMap.delete(name)
  }

  getCacheName(config) {
    if (!config) return ''
    const { nameLimit } = requestCache
    // 兼容 string
    if (typeof config === 'string') {
      if (config.length > nameLimit) return ''
      return config
    }
    // 通过排序请求对象属性来生成 name
    const name = Object.keys(config)
      .sort()
      .map(key => {
        let value = config[key]
        if (typeof value === 'object') value = JSON.stringify(value)
        return `${key}=${value}`
      })
      .join('&')
    // 检测长度
    if (name.length > nameLimit) return ''
    return name
  }

  getCacheTime(config) {
    const time = config && config._cacheTime
    if (time && typeof time === 'number') return time
    return 0
  }
}
